package lib.data;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

import lib.base.EgoSystem;

public abstract class Data
{
	protected Map<String, Object> field;
	
	private String _table;
	private Map<String, String> _primaryKeys; /* [key] => [value] */

	/**
	 * This is an abstract Class for Data Classes
	 * This provides DB methods
	 * 
	 * @param table
	 * @param primaryKeys
	 * @throws Exception
	 */
	public Data(String table) throws Exception
	{
		_table = table;
		_primaryKeys = new HashMap<String, String>();
	}
	
	protected void addPrimaryKey(String k, String v)
	{
		_primaryKeys.put(k, v);
	}
	
	protected void loadData() throws Exception
	{
		field = new HashMap<String, Object>();
		
		// sql query to fill field
		Statement stm = (Statement) EgoSystem.dbConnection.createStatement();
		ResultSet rs = stm.executeQuery(
			"SELECT * FROM `"+_table+"` WHERE "+_getPrimaryKeysAND()
		);
		ResultSetMetaData rsmd = rs.getMetaData();
		int nCols = rsmd.getColumnCount();
		while (rs.next())
		{
			// begin with 1 instead of 0
			for (int i=1;i<=nCols;i++)
			{
				field.put(
					rsmd.getColumnName(i),
					rs.getObject(i)
				);
			}
		}
		rs.close();
		stm.close();
	}
	
	/**
	 * returns and sql WHERE String with all primarys keys and values
	 * @return
	 */
	private String _getPrimaryKeysAND()
	{
		String and = "1=1";
		for (Map.Entry<String, String> entry : _primaryKeys.entrySet())
		{
			and += " AND " + entry.getKey() + "='" + entry.getValue() + "'";
		}
		return and;
	}
	
	/**
	 * returns original field (no inherit)
	 * 
	 * @param k
	 * @return
	 */
	public Object getOriginalField(String k)
	{
		return field.get(k);
	}
	
	public Object getField(String k)
	{
		return field.get(k);
	}
	
	public Map<String, Object> getFieldMap()
	{
		return field;
	}
	
	/**
	 * sets a field
	 * 
	 * @param k key
	 * @param v value
	 */
	public void setField(String k, String v)
	{
		if (_primaryKeys.get(k) != null) return; // don't change the primaryKey's Value
		field.put(k, v);
	}
	
	public void update() throws Exception
	{
		String set = "SET ";
		boolean first=true;
		for (Map.Entry<String, Object> entry : field.entrySet())
		{
			if (_primaryKeys.get(entry.getKey()) != null) continue; // don't change the primaryKey's Value
			
			String value = entry.getValue().toString();
			if (entry.getValue().toString().equals("true") || entry.getValue().toString().equals("false"))
			{ // boolean
				if (entry.getValue().toString().equals("true"))
				{
					value = "1";
				} else
				{
					value = "0";
				}
			} else
			{
				// is it a string?
				try {
					Integer.parseInt(entry.getValue().toString());
				} catch (NumberFormatException e)
				{
					// string
					value = "'"+entry.getValue().toString()+"'";
				}
			}
			set += (first ? "" : ", ")+entry.getKey()+"="+value;
			first = false;
		} // otherwise it's a number

		// sql query to fill field
		String query = "UPDATE `"+_table+"` " + set + " WHERE "+_getPrimaryKeysAND();
		try {
			Statement stm = (Statement) EgoSystem.dbConnection.createStatement();
			stm.executeUpdate(
				query
			);
			stm.close();
		} catch (Exception e)
		{
			throw new Exception("SQL error:\n"+e.toString()+"\nSQL Query:"+query);
		}
	}
	
	public void update(Map<String, Object> data) throws Exception
	{
		boolean changes = false;
		
		// compare
		for (Map.Entry<String, Object> entry : data.entrySet())
		{
			if (
				getOriginalField(entry.getKey()) == null ||
				entry.getValue() == null
			) continue;
			
			if (!entry.getValue().toString().equals( getOriginalField(entry.getKey()).toString() ))
			{
				// changes
				changes = true;
				setField(entry.getKey(), entry.getValue().toString());
			}
		}
		if (changes)
		{
			update();
		}
	}
	
	/**
	 * delete its own entry from DB
	 * @throws Exception
	 */
	public void delete() throws Exception
	{
		Statement stm = EgoSystem.dbConnection.createStatement();
		String sql = "DELETE FROM `"+_table+"` WHERE "+_getPrimaryKeysAND();
		stm.execute(sql);
		stm.close();
	}
}
